import types


class DotFieldObject:
    def __init__(self, d:dict):
        self._dict = d

    def __dict__(self):
        return self.get_dict()

    def get_dict(self):
        return {key: to_value(self._dict[key]) for key in self._dict}

    def keys(self):
        return self._dict.keys()

    def values(self):
        return self._dict.values()

    def copy(self):
        return DotFieldObject(self._dict.copy())

    def clear(self):
        self._dict.clear()

    def pop(self, k):
        self._dict.pop(k)

    def __contains__(self, item):
        return item in self._dict

    def __delitem__(self, key):
        del self._dict[key]

    def __delattr__(self, item):
        del self._dict[item]

    def __iter__(self):
        return self._dict.__iter__()

    def __getattribute__(self, item):
        if item.startswith('_') or item not in self._dict:
            return super(DotFieldObject, self).__getattribute__(item)
        else:
            return to_obj(self._dict[item])

    def __getitem__(self, item):
        return self.__getattribute__(item)

    def __setattr__(self, item, value):
        if item.startswith('_') or item not in self._dict and hasattr(self, item):
            super(DotFieldObject, self).__setattr__(item, value)
        else:
            self._dict[item] = value

    def __setitem__(self, item, value):
        return self.__setattr__(item, value)

    def __repr__(self):
        return str(self._dict)


def to_obj(value):
    return DotFieldObject(value) if isinstance(value, dict) else \
        [to_obj(v) for v in value] if isinstance(value, list) else \
            (to_obj(v) for v in value) if isinstance(value, tuple) else \
                {to_obj(v) for v in value} if isinstance(value, set) else \
                    gen_obj(value) if isinstance(value, types.GeneratorType) else value

def to_value(obj):
    return {key: to_value(obj[key]) for key in obj} if isinstance(obj, dict) else \
        [to_value(v) for v in obj] if isinstance(obj, list) else \
            (to_value(v) for v in obj) if isinstance(obj, tuple) else \
                {to_value(v) for v in obj} if isinstance(obj, set) else \
                    obj.get_dict() if isinstance(obj, DotFieldObject) else \
                        gen_value(obj) if isinstance(obj, types.GeneratorType) else obj


def gen_obj(generator):
    for obj in generator:
        yield to_obj(obj)



def gen_value(generator):
    for obj in generator:
        yield to_value(obj)


if __name__ == '__main__':
    a = {'abc': 123, 'defg': 'hahaha', 'hij': True, 'klm': [123, {'a': 1, 'b':2, 'c':3}]}
    a = to_obj(a)
    print(a.abc, a.defg, a.hij, a.klm, a.klm[1].a, a.klm[1].b, a.klm[1].c, sep='\n')
    print(a.copy())